Unit BCodeGenUnit;

{$mode objfpc}{$H+}

Interface

Uses
  Classes, SysUtils, strutils, BCommonUnit, BListsUnit, BPeaUnit, BWebJSONUnit,
    BGeneratorUnit, BModelGeneratorUnit, BHolderGeneratorUnit,
    BControllerGeneratorUnit, BSQLGeneratorUnit, BCommonConstsGeneratorUnit,
    BConstsGeneratorUnit;

Type

{ BCodeGenClass }

 BCodeGenClass = Class
  Private
    bPeas: BPeasList;
    bProjectPath: String;
    bPeasPath: String;
    bModelsPath: String;
    bControllersPath: String;
    bSQLScriptsPath: String;
    bTestsPath: String;
    bUnitsPath: String;
    bConstsPath: ansistring;

    bModelGenerator: BModelGeneratorClass;
    bProjectName: String;

    Function GetPath(Const aDirectories: Array Of String): String;
    Procedure SetProjectPath(aValue: String);

    Procedure LoadPeas;
    Function FindPea(Const aCaption: String): BPeaClass;

    Procedure UpdateCommonConstsFile;
    Procedure UpdateModel(Const aPea: BPeaClass);
    Procedure UpdateController(Const aPea: BPeaClass);
    Procedure UpdateSQLScript(Const aPea: BPeaClass);
    Procedure UpdateConsts(Const aPea: BPeaClass);
  Public
    Property ProjectPath: String Read bProjectPath Write SetProjectPath;

    Procedure UpdatePea(Const aPea: BPeaClass);
    Procedure UpdateAll;

    Constructor Build;
    Destructor Burn;
End;

Implementation

{ BCodeGenClass }

Function BCodeGenClass.GetPath(Const aDirectories: Array Of String): String;
Var
  i: Integer;
Begin
  Result := DirectorySeparator;
  For i := Low(aDirectories) To High(aDirectories) Do
    Result += aDirectories[i] + DirectorySeparator;
End;

Procedure BCodeGenClass.SetProjectPath(aValue: String);
Begin
  If bProjectPath=aValue Then Exit;
  bProjectPath:=aValue;

  bPeasPath := bProjectPath + GetPath(['Peas']);
  If Not(DirectoryExists(bPeasPath)) Then
    MkDir(bPeasPath);

  bModelsPath := bProjectPath + GetPath(['Peas', 'Models']);
  If Not(DirectoryExists(bModelsPath)) Then
    MkDir(bModelsPath);

  bControllersPath := bProjectPath + GetPath(['Peas', 'Controllers']);
  If Not(DirectoryExists(bControllersPath)) Then
    MkDir(bControllersPath);

  bSQLScriptsPath := bProjectPath + GetPath(['Peas', 'SQL']);
  If Not(DirectoryExists(bSQLScriptsPath)) Then
    MkDir(bSQLScriptsPath);

  bTestsPath := bProjectPath + GetPath(['Peas', 'Tests']);
  If Not(DirectoryExists(bTestsPath)) Then
    MkDir(bTestsPath);

  bConstsPath := bProjectPath + GetPath(['Peas', 'Consts']);
  If Not(DirectoryExists(bConstsPath)) Then
    MkDir(bConstsPath);

  bUnitsPath := bProjectPath + GetPath(['Peas', 'SQL']);
  If Not(DirectoryExists(bTestsPath)) Then
    MkDir(bTestsPath);

  If FileExists(bProjectPath + GetPath(['Peas']) + 'peas.json') Then
    LoadPeas;
End;

Procedure BCodeGenClass.UpdateAll;
Var
  i: Integer;
Begin
  UpdateCommonConstsFile;
  For i := 0 To bPeas.Count - 1 Do
    Begin
      WriteLn(i, ': ', bPeas[i].Caption);
      UpdatePea(bPeas[i]);
    End;
End;

Procedure BCodeGenClass.UpdatePea(Const aPea: BPeaClass);
Begin
  UpdateModel(aPea);
  UpdateController(aPea);
  UpdateSQLScript(aPea);
  UpdateConsts(aPea);
End;

Procedure BCodeGenClass.UpdateModel(Const aPea: BPeaClass);
Var
  aGenerator: BGeneratorClass = nil;
Begin
  Case aPea.Kind Of
    pkPlain, pkTree:
      aGenerator := BModelGeneratorClass.Build(bPeasPath, aPea, bProjectName);
    pkHolder:
      aGenerator := BHolderGeneratorClass.Build(bPeasPath, aPea, bProjectName);
  End;
  aGenerator.Update;
  aGenerator.SavePeaUnit;
  aGenerator.Burn;
End;

Procedure BCodeGenClass.UpdateController(Const aPea: BPeaClass);
Begin
  With BControllerGeneratorClass.Build(bPeasPath, aPea, bProjectName) Do
    Begin
      Update;
      SavePeaUnit;
      Burn;
    End;
End;

Procedure BCodeGenClass.UpdateSQLScript(Const aPea: BPeaClass);
Var
  aGenerator: BSQLGeneratorClass;
Begin
  aGenerator :=  BSQLGeneratorClass.Build(bPeasPath, aPea, bPeas, bProjectName);
  aGenerator.Update;
  aGenerator.SavePeaUnit;
  aGenerator.Burn;
End;

Procedure BCodeGenClass.UpdateConsts(Const aPea: BPeaClass);
Var
  aGenerator: BConstsGeneratorClass;
Begin
  aGenerator :=  BConstsGeneratorClass.Build(bPeasPath, aPea, bProjectName);
  If aGenerator.HasEnumProperties Then
    Begin
      aGenerator.Update;
      aGenerator.SavePeaUnit;
    End;
  aGenerator.Burn;
End;

Function BCodeGenClass.FindPea(Const aCaption: String): BPeaClass;
Var
  i: Integer;
Begin
  Result := nil;
  For i := 0 To bPeas.Count - 1 Do
    If bPeas[i].Caption = aCaption Then
      Exit(bPeas[i]);
End;

Procedure BCodeGenClass.UpdateCommonConstsFile;
Var
  aGenerator: BCommonConstsGeneratorClass;
Begin
  aGenerator :=  BCommonConstsGeneratorClass.Build(bProjectPath + '/Peas/',
    bPeas, bProjectName);
  aGenerator.Update;
  aGenerator.SavePeaUnit;
  aGenerator.Burn;
End;

Procedure BCodeGenClass.LoadPeas;
Var
  i, j: Integer;
  aPeasJSON: String = '';
  aStringList: TStringList;
  aPeaJSON, aPropertyJSON: String;
  aPeasJSONList, aPropertiesJSONList: BStringsList;

  aPea: BPeaClass;
  aPeaProperty: BPeaPropertyClass;
Begin
  aStringList := TStringList.Create;
  Try
    aStringList.LoadFromFile(bProjectPath + GetPath(['Peas']) + 'peas.json');
    For i := 0 To  aStringList.Count - 1 Do
      aPeasJSON += Trim(aStringList[i]);
  Finally
    aStringList.Free;
  End;
  aPeasJSON := StringReplace(aPeasJSON, '": ', '":', [rfReplaceAll]);
  aPeasJSON := StringReplace(aPeasJSON, '}, {', '},{', [rfReplaceAll]);

  aPeasJSONList := BStringsList.Build;
  aPropertiesJSONList := BStringsList.Build;
  Try
    bProjectName := GetJSStr(aPeasJSON, 'projectName');
    GetJSObjList(GetJSArr(aPeasJSON, 'peas'), aPeasJSONList);
    For i := 0 To aPeasJSONList.Count - 1 Do
      Begin
        aPeaJSON := aPeasJSONList[i];
        aPea := BPeaClass.Build(GetJSInt(aPeaJSON, 'id'),
          GetJSStr(aPeaJSON, 'kind'), GetJSStr(aPeaJSON, 'caption'),
          GetJSStr(aPeaJSON, 'pluralCaption'),
          FindPea(GetJSStr(aPeaJSON, 'pea')));
        bPeas.Add(aPea);

        GetJSObjList(GetJSArr(aPeaJSON, 'properties'), aPropertiesJSONList);
        For j := 0 To aPropertiesJSONList.Count - 1 Do
          Begin
            aPropertyJSON := aPropertiesJSONList[j];
            aPeaProperty := BPeaPropertyClass.Build(
              GetJSStr(aPropertyJSON, 'caption'),
              GetJSStr(aPropertyJSON, 'pluralCaption'),
              GetJSStr(aPropertyJSON, 'sqlCaption'),
              GetJSStr(aPropertyJSON, 'kind'),
              GetJSObj(aPropertyJSON, 'enumInfo'));

            If aPeaProperty.Kind In [ppkPea, ppkPeas, ppkHolders] Then
              aPeaProperty.Pea := FindPea(GetJSStr(aPropertyJSON, 'pea'));
            aPea.AddProperty(aPeaProperty);
          End;
      End;
  Finally
    aPropertiesJSONList.Burn;
    aPeasJSONList.Burn;
  End;
End;

Constructor BCodeGenClass.Build;
Begin
  bPeas := BPeasList.Build;
End;

Destructor BCodeGenClass.Burn;
Begin
  bPeas.Burn;
End;

End.
